﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web.Security;
using System.Web;
using System.Configuration.Provider;
using System.Configuration;
using System.DirectoryServices;
using System.Collections;
//using LinqToLdap;
using NotAClue;

namespace NotAClue.Web.Security
{
    public class ActiveDirectoryRolesProvider : RoleProvider
    {
        private const string CONNECTION_STRING_NAME = "connectionStringName";
        private const string ATTRIBUTE_MAP_USERNAME = "attributeMapUsername";
        private const string CONNECTION_USERNAME = "connectionUsername";
        private const string CONNECTION_PASSWORD = "connectionPassword";
        private const string APPLICATION_NAME = "applicationName";
        private const string CONNECTION_PROTECTION = "connectionProtection";
        private const string CACHE_TIMEOUT_MINUTES = "cacheTimeoutMinutes";

        private String _applicationName = string.Empty;
        private String _loginProperty = "sAMAccountName";
        private String _connectionUsername = string.Empty;
        private String _connectionPassword = string.Empty;
        private String _connectionProtection = string.Empty;
        private int _cacheTimeoutMinutes;

        //connectionString="LDAP://DC1.YourDomain.com/DC=YourDomain,DC=com"
        private string _connectionString = string.Empty;
        private ConnectionStringSettings _connectionStringSettings;

        private Dictionary<String, List<String>> _userRoles;
        private DateTime _timeout;

        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            if (name == null || name.Length == 0)
                name = "STS_AD_RoleProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Basic Active Directory Role provider");
            }

            if (!string.IsNullOrEmpty(config[ATTRIBUTE_MAP_USERNAME]))
                _loginProperty = config[ATTRIBUTE_MAP_USERNAME];

            if (!string.IsNullOrEmpty(config[CONNECTION_USERNAME]))
                _connectionUsername = config[CONNECTION_USERNAME];

            if (!string.IsNullOrEmpty(config[CONNECTION_PASSWORD]))
                _connectionPassword = config[CONNECTION_PASSWORD];

            if (!string.IsNullOrEmpty(config[CONNECTION_PROTECTION]))
                _connectionProtection = config[CONNECTION_PROTECTION];

            if (!string.IsNullOrEmpty(config[CACHE_TIMEOUT_MINUTES]))
                _cacheTimeoutMinutes = Convert.ToInt32(config[CACHE_TIMEOUT_MINUTES]);
            else
                _cacheTimeoutMinutes = 4 * 60; // default is four hours

            _applicationName = config[APPLICATION_NAME];

            // Initialize the abstract base class.
            base.Initialize(name, config);

            // get connection string
            _connectionStringSettings = ConfigurationManager.ConnectionStrings[config[CONNECTION_STRING_NAME]];

            if (_connectionStringSettings == null || _connectionStringSettings.ConnectionString.Trim() == "")
                throw new ProviderException("Connection string cannot be blank.");

            _connectionString = _connectionStringSettings.ConnectionString;

            _userRoles = new Dictionary<string, List<string>>();
            _timeout = DateTime.Now.AddMinutes(_cacheTimeoutMinutes);
        }

        #region implemented
        public override bool IsUserInRole(string username, string roleName)
        {
            var usersRoles = GetRolesForUser(username);
            return usersRoles.Contains(roleName);
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            throw new NotImplementedException();
        }

        public override string[] GetRolesForUser(string userName)
        {
            // return cached user roles
            if (_userRoles.Keys.Contains(userName) && DateTime.Now < _timeout)
            {
                return _userRoles[userName].ToArray();
            }
            else
            {
                _userRoles = new Dictionary<string, List<string>>();
                _timeout = DateTime.Now.AddMinutes(_cacheTimeoutMinutes);
            }

            var allRoles = new List<String>();
            var root = new DirectoryEntry(_connectionString);

            if (!String.IsNullOrEmpty(_connectionUsername) && !String.IsNullOrEmpty(_connectionPassword))
            {
                root.Username = _connectionUsername;
                root.Password = _connectionPassword;
            }

            if (!String.IsNullOrEmpty(_connectionProtection))
                root.AuthenticationType = _connectionProtection.ToEnum<AuthenticationTypes>();

            var startOfName = userName.IndexOf("\\") + 1;
            var justUserName = userName.Substring(startOfName, userName.Length - startOfName);
            //allRoles = root.GetRolesForUserRecursive(justUserName, _loginProperty);
            allRoles = root.GetRolesForUser(justUserName, _loginProperty);

            // make sure they are a member of Domain Users
            allRoles.Add("Domain Users");
            // cache roles
            _userRoles.Add(userName, allRoles);

            return allRoles.ToArray();
        }

        public override string[] GetUsersInRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool RoleExists(string roleName)
        {
            throw new NotImplementedException();
        }

        public override string[] GetAllRoles()
        {
            throw new NotImplementedException();
        }
        #endregion

        #region Helpers
        #endregion

        #region Not Implemented
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }

        public override void CreateRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            throw new NotImplementedException();
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}
